package com.asen.commons.utils;

import com.asen.commons.core.base.enums.DecimalTypeEnum;
//import com.asen.commons.base.enums.SysParamEnum;
//import com.asen.commons.base.enums.SystemParamEnum;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * decimal处理工具类
 *
 * @author Gary
 * @version 1.0
 * @date 2019-08-27
 */
public class DecimalUtils {

    /**
     * 0.01
     */
    public final static BigDecimal POINT_ZERO_ONE = new BigDecimal("0.01");

    /**
     * 三十 thirty 30
     */
    public final static BigDecimal THIRTY = new BigDecimal("30");

    /**
     * 六十 sixty 60
     */
    public final static BigDecimal SIXTY = new BigDecimal("60");

    /**
     * 九十 ninety 90
     */
    public final static BigDecimal NINETY = new BigDecimal("90");

    /**
     * 一百 hundred 100
     */
    public final static BigDecimal HUNDRED = new BigDecimal("100");

    /**
     * 一万 ten thousand 10000
     */
    public final static BigDecimal TEN_THOUSAND = new BigDecimal("10000");

    /**
     * 十万 ten thousand 100000
     */
    public final static BigDecimal HUNDRED_THOUSAND = new BigDecimal("100000");

    /**
     * 百万 ten thousand 10000
     */
    public final static BigDecimal MILLION = new BigDecimal("1000000");

    /**
     * 负99999 -99999
     */
    public final static BigDecimal FU_DECIMAL = new BigDecimal("-99999");

    /**
     * 9999999999
     */
    public final static BigDecimal BIG_NUMBER = new BigDecimal("9999999999");

    public static boolean isEmpty(BigDecimal value) {
        if (value == null) {
            return true;
        }
        return BigDecimal.ZERO.compareTo(value) == 0;
    }

    public static boolean isNotEmpty(BigDecimal value) {
        return !isEmpty(value);
    }

    public static BigDecimal sure(BigDecimal value) {
        if (value == null) {
            value = BigDecimal.ZERO;
        }
        return value;
    }

    public static BigDecimal abs(BigDecimal value) {
        if (value == null) {
            value = BigDecimal.ZERO;
        }
        return value.abs();
    }

    public static BigDecimal add(BigDecimal val1, BigDecimal val2) {
        return sure(val1).add(sure(val2));
    }

    public static BigDecimal add(BigDecimal val1, BigDecimal val2, DecimalTypeEnum decimalType) {
        return setScale(sure(val1).add(sure(val2)), decimalType);
    }

    public static BigDecimal addMore(BigDecimal... vals) {
        BigDecimal sum = BigDecimal.ZERO;
        if (vals != null && vals.length > 0) {
            for (BigDecimal val : vals) {
                sum = add(sum, val);
            }
        }
        return sum;
    }

    public static BigDecimal subtract(BigDecimal val1, BigDecimal val2) {
        return sure(val1).subtract(sure(val2));
    }

    public static BigDecimal subtract(BigDecimal val1, BigDecimal val2, DecimalTypeEnum decimalType) {
        return setScale(sure(val1).subtract(sure(val2)), decimalType);
    }

    public static BigDecimal multiply(BigDecimal val1, BigDecimal val2) {
        return sure(val1).multiply(sure(val2));
    }

    public static BigDecimal multiply(BigDecimal val1, BigDecimal val2, DecimalTypeEnum decimalType) {
        return setScale(sure(val1).multiply(sure(val2)), decimalType);
    }

    public static BigDecimal multiply(BigDecimal val1, BigDecimal val2, int scale) {
        return sure(val1).multiply(sure(val2)).setScale(scale, RoundingMode.HALF_UP);
    }

    public static BigDecimal divide(BigDecimal decimal1, BigDecimal decimal2) {
        decimal1 = sure(decimal1);
        decimal2 = sure(decimal2);
        if (BigDecimal.ZERO.compareTo(decimal1) == 0 || BigDecimal.ZERO.compareTo(decimal2) == 0) {
            return BigDecimal.ZERO;
        }

        return decimal1.divide(decimal2, 10, BigDecimal.ROUND_HALF_UP);
    }

    public static BigDecimal divide(BigDecimal decimal1, BigDecimal decimal2, DecimalTypeEnum decimalType) {
        return setScale(divide(decimal1, decimal2), decimalType);
    }

    public static BigDecimal divide(BigDecimal decimal1, BigDecimal decimal2, int scale) {
        decimal1 = sure(decimal1);
        decimal2 = sure(decimal2);
        if (BigDecimal.ZERO.compareTo(decimal1) == 0 || BigDecimal.ZERO.compareTo(decimal2) == 0) {
            return BigDecimal.ZERO;
        }

        return decimal1.divide(decimal2, scale, BigDecimal.ROUND_HALF_UP);
    }

    public static int compareTo(BigDecimal decimal1, BigDecimal decimal2) {
        return sure(decimal1).compareTo(sure(decimal2));
    }

    /**
     * 比较绝对值大小
     *
     * @param decimal1
     * @param decimal2
     * @return
     */
    public static int compareToByAbs(BigDecimal decimal1, BigDecimal decimal2) {
        return sure(decimal1).abs().compareTo(sure(decimal2).abs());
    }

    public static int getScale(DecimalTypeEnum decimalType) {
        int scale = -1;
        if (decimalType == null) {
            return scale;
        }

        // TODO Decimal精度
        switch (decimalType) {
            case AMOUNT:
//                scale = SysParamUtils.getInt(SystemParamEnum.DECIMAL_SCALE, SysParamEnum.DECIMAL_SCALE_OF_AMOUNT);
                scale = 2;
                break;
            case PRICE:
//                scale = SysParamUtils.getInt(SystemParamEnum.DECIMAL_SCALE, SysParamEnum.DECIMAL_SCALE_OF_PRICE);
                scale = 8;
                break;
            case QTY:
//                scale = SysParamUtils.getInt(SystemParamEnum.DECIMAL_SCALE, SysParamEnum.DECIMAL_SCALE_OF_QTY);
                scale = 4;
                break;
            case RATE:
//                scale = SysParamUtils.getInt(SystemParamEnum.DECIMAL_SCALE, SysParamEnum.DECIMAL_SCALE_OF_RATE);
                scale = 8;
                break;
            case EXCHANGE_RATE:
//                scale = SysParamUtils.getInt(SystemParamEnum.DECIMAL_SCALE, SysParamEnum.DECIMAL_SCALE_OF_EXCHANGE_RATE);
                scale = 8;
                break;
            case TAX_RATE:
//                scale = SysParamUtils.getInt(SystemParamEnum.DECIMAL_SCALE, SysParamEnum.DECIMAL_SCALE_OF_TAX_RATE);
                scale = 8;
                break;
            default:
        }

        return scale;
    }

    public static BigDecimal setScale(BigDecimal value, DecimalTypeEnum decimalType) {
        if (value == null) {
            return BigDecimal.ZERO;
        }

        int scale = getScale(decimalType);
        if (scale < 0) {
            return value;
        }

        return value.setScale(scale, BigDecimal.ROUND_HALF_UP);
    }

    public static BigDecimal setScale(BigDecimal value, int scale) {
        return sure(value).setScale(scale, BigDecimal.ROUND_HALF_UP);
    }


    public static BigDecimal convertStringToAmount(String decimalStr) {
        BigDecimal rate = BigDecimal.ONE;

        if (StringUtils.isEmpty(decimalStr)) {
            return BigDecimal.ZERO;
        }

        String regEx = "[^(-?0-9).]";
        Pattern p = Pattern.compile(regEx);
        Matcher m = p.matcher(decimalStr);
        String amountStr = m.replaceAll("").trim();
        BigDecimal sourceAmount = new BigDecimal(amountStr);

        if (decimalStr.indexOf("美元") > 0) {
            rate = new BigDecimal("7.0786");
        }
        if (decimalStr.indexOf("万") > 0) {
            sourceAmount = sourceAmount.multiply(new BigDecimal(10000));
        }

        return sourceAmount;
    }

    /**
     * 提取字符串中的数字并拼成bigdecimal
     *
     * @param decimalStr
     * @return
     */
    public static BigDecimal convertStringToBigdecimal(String decimalStr) {
        if (StringUtils.isEmpty(decimalStr)) {
            return BigDecimal.ZERO;
        }
        String regEx = "[^(-?0-9).]";
        Pattern p = Pattern.compile(regEx);
        Matcher m = p.matcher(decimalStr);
        String amountStr = m.replaceAll("").trim();
        BigDecimal sourceAmount = null;
        if (StringUtils.isNotEmpty(amountStr)) {
            sourceAmount = new BigDecimal(amountStr);
        } else {
            sourceAmount = BigDecimal.ZERO;
        }

        return sourceAmount;
    }

    /**
     * 获取百分比对应小数,例如 50 -> 0.5
     *
     * @param value
     * @return
     */
    public static BigDecimal getPercentage(BigDecimal value) {
        if (value == null) {
            return BigDecimal.ZERO;
        }
        return value.divide(HUNDRED, 10, BigDecimal.ROUND_HALF_UP);
    }

    /**
     * 获取百分比对应小数加1,例如 50 -> 0.5 + 1 = 1.5
     *
     * @param value
     * @return
     */
    public static BigDecimal getPercentageAddOne(BigDecimal value) {
        return add(BigDecimal.ONE, getPercentage(value));
    }

    /**
     * 获取 1- 百分比对应小数,例如 30 -> 0.3 -> 1 - 0.3 = 0.7
     *
     * @param value
     * @return
     */
    public static BigDecimal getPercentageSubOne(BigDecimal value) {
        return subtract(BigDecimal.ONE, getPercentage(value));
    }

    public static boolean greater(BigDecimal one, BigDecimal two) {
        return compareTo(one, two) > 0;
    }

    public static boolean greaterEqual(BigDecimal one, BigDecimal two) {
        return compareTo(one, two) >= 0;
    }

    public static boolean less(BigDecimal one, BigDecimal two) {
        return compareTo(one, two) < 0;
    }

    public static boolean lessEqual(BigDecimal one, BigDecimal two) {
        return compareTo(one, two) <= 0;
    }

    public static boolean equal(BigDecimal one, BigDecimal two) {
        return compareTo(one, two) == 0;
    }

    public static boolean notEqual(BigDecimal one, BigDecimal two) {
        return compareTo(one, two) != 0;
    }

    public static boolean greaterZero(BigDecimal one) {
        return greater(one, BigDecimal.ZERO);
    }

    public static boolean greaterEqualZero(BigDecimal one) {
        return greaterEqual(one, BigDecimal.ZERO);
    }

    public static boolean lessZero(BigDecimal one) {
        return less(one, BigDecimal.ZERO);
    }

    public static boolean lessEqualZero(BigDecimal one) {
        return lessEqual(one, BigDecimal.ZERO);
    }

    public static boolean equalZero(BigDecimal one) {
        return equal(one, BigDecimal.ZERO);
    }

    public static boolean equalZero(BigDecimal... values) {
        if (values == null || values.length == 0) {
            return false;
        }
        boolean isZero = equalZero(values[0]);
        for (BigDecimal value : values) {
            isZero = isZero && equalZero(value);
        }
        return isZero;
    }

    public static boolean notEqualZero(BigDecimal one) {
        return !equal(one, BigDecimal.ZERO);
    }

    /**
     * 取反
     *
     * @param val
     * @return
     */
    public static BigDecimal negate(BigDecimal val) {
        return sure(val).negate();
    }

    /**
     * 取反,根据参数判断是否取反
     *
     * @param val
     * @return
     */
    public static BigDecimal negateIs(BigDecimal val, boolean isNegate) {
        return isNegate ? sure(val).negate() : sure(val);
    }

    /**
     * 取最小
     */
    public static BigDecimal min(BigDecimal... values) {
        if (values == null || values.length == 0) {
            return BigDecimal.ZERO;
        }
        BigDecimal min = sure(values[0]);
        for (int i = 0; i < values.length; i++) {
            if (less(values[i], min)) {
                min = sure(values[i]);
            }
        }
        return min;
    }

    /**
     * 取最大
     */
    public static BigDecimal max(BigDecimal... values) {
        if (values == null || values.length == 0) {
            return BigDecimal.ZERO;
        }
        BigDecimal max = sure(values[0]);
        for (int i = 0; i < values.length; i++) {
            if (greater(values[i], max)) {
                max = sure(values[i]);
            }
        }
        return max;
    }

    public static BigDecimal mul(BigDecimal... values) {
        if (values == null || values.length == 0) {
            return BigDecimal.ZERO;
        }
        BigDecimal val = values[0];
        for (int i = 1; i < values.length; i++) {
            val = setScale(multiply(val, values[i]), 20);
        }
        return val;
    }

    public static BigDecimal mul(DecimalTypeEnum typeEnum, BigDecimal... values) {
        return DecimalUtils.setScale(mul(values), DecimalTypeEnum.AMOUNT);
    }

    public static BigDecimal parse(Object o) {
        if (o == null) {
            return BigDecimal.ZERO;
        }
        if (o instanceof BigDecimal) {
            return (BigDecimal) o;
        }
        String s = StringUtils.toString(o);
        if (StringUtils.isBlank(s)) {
            return BigDecimal.ZERO;
        }
        if (s.contains("-") || s.contains("-") || s.contains("*") || s.contains("/")) {
            // 四则运算
            MathExpressUtil operator = new MathExpressUtil();
            Object resultValue = operator.caculateFormula(s);
            s = StringUtils.toString(resultValue);
        }
        return new BigDecimal(s);
    }

    public static String toString(BigDecimal val, DecimalTypeEnum typeEnum) {
        return setScale(sure(val), typeEnum).toPlainString();
    }

    public static String toString(BigDecimal val) {
        return sure(val).toPlainString();
    }

    public static String toStringNotZore(BigDecimal val) {
        return sure(val).stripTrailingZeros().toPlainString();
    }

    public static String toStringNotZore(BigDecimal val, DecimalTypeEnum typeEnum) {
        return setScale(sure(val), typeEnum).stripTrailingZeros().toPlainString();
    }

    /**
     * 获取金额格式字符串
     *
     * @param val
     * @return
     */
    public static String getAmtStr(BigDecimal val) {
        return toStringNotZore(val, DecimalTypeEnum.AMOUNT);
    }

    /**
     * 获取单价格式字符串
     *
     * @param val
     * @return
     */
    public static String getPriceStr(BigDecimal val) {
        return toStringNotZore(val, DecimalTypeEnum.PRICE);
    }

    /**
     * 获取数量格式字符串
     *
     * @param val
     * @return
     */
    public static String getQtyStr(BigDecimal val) {
        return toStringNotZore(val, DecimalTypeEnum.QTY);
    }

    /**
     * 获取汇率值
     *
     * @param val
     * @return
     */
    public static BigDecimal getRate(BigDecimal val) {
        return lessZero(val) ? BigDecimal.ONE : val;
    }

    public static BigDecimal getRadio(BigDecimal value1, BigDecimal value2) {
        if (equalZero(value1) && equalZero(value2)) {
            return BigDecimal.ZERO;
        }
        if (equalZero(value1)) {
            return negate(HUNDRED);
        }
        if (equalZero(value2)) {
            return HUNDRED;
        }
        return DecimalUtils.multiply(DecimalUtils.divide(value1, value2), DecimalUtils.HUNDRED, DecimalTypeEnum.RATE);
    }

    public static BigDecimal getRptRadio(BigDecimal value1, BigDecimal value2) {
        if (equalZero(value1) && equalZero(value2)) {
            return BigDecimal.ZERO;
        }
        if (equalZero(value2)) {
            return HUNDRED;
        }
        if (equalZero(value1)) {
            return negate(HUNDRED);
        }
        // 同比 环比 = (本期-上期) / 上期 * 100
        return DecimalUtils.multiply(DecimalUtils.divide(DecimalUtils.subtract(value1, value2), value2), DecimalUtils.HUNDRED, DecimalTypeEnum.RATE);
    }

    /**
     * 转换成千分位格式字符串
     *
     * @return
     */
    public static String formatToThousandths(BigDecimal val, int scale) {
        if (val == null) {
            return null;
        }
        String pattern = null;
        if (scale == 1) {
            //保留一位小数
            pattern = ",###,##0.0";
        } else if (scale == 2) {
            //保留二位小数
            pattern = ",###,##0.00";
        } else {
            //默认没有小数位
            pattern = ",###,##0";
        }
        DecimalFormat df = new DecimalFormat(pattern);
        return df.format(val);
    }

}
